﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.DL.Data.Common;
using ScrumTable.DL.Data.Generic.Cache;

#endregion

namespace ScrumTable.DL.Data.Generic
{
    /// <summary>
    /// The attachment data access object encapsulates all service routines in order
    /// to access data from an external system.
    /// </summary>
    /// <remarks>The implementation of the data object class follows the Data Access Object pattern.</remarks>
    internal class AttachmentDataAccessObject : DataAccessObjectBase
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new AttachmentDataAccessObject instance, no public constructor
        /// needed.
        /// </summary>
        internal AttachmentDataAccessObject(GenericDataContextBase context)
            : base(context)
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new IDataObject below the given parent id chain.
        /// </summary>
        /// <param name="parentIdChain">Global unique ids of the parent objects.</param>
        /// <returns>Returns the created object.</returns>
        public override IDataObject Insert(ValueChain<string> parentIdChain)
        {
            PreCondition.AssertNotNull(parentIdChain, "parentIdChain");

            using (DataAccessSection data = EnterDataAccessSection(parentIdChain))
            {
                if (data.CurrentProject == null)
                    return null;

                return InitAttachmentObject(data.CurrentProject.CreateAttachment(parentIdChain));
            }
        }

        /// <summary>
        /// Moves a IDataObject using the given id into a new parent object.
        /// </summary>
        /// <param name="parentIdChain">Global unique ids of the parent objects.</param>
        /// <param name="toMove">Specifies the data object to move inside the data storage.</param>
        /// <param name="newParentIdChain">Specifies the new parent id chain.</param>
        public override void Update(ValueChain<string> parentIdChain, IDataObject toMove, ValueChain<string> newParentIdChain)
        {
            // not supported (yet)
        }

        /// <summary>
        /// Retrieves all data objects which are stored in conjunction
        /// with the specified arguments.
        /// </summary>
        /// <param name="parentIdChain">Global unique ids of the parent objects.</param>
        /// <returns>Returns the retrieved data objects.</returns>
        public override ICollection<IDataObject> GetAll(ValueChain<string> parentIdChain)
        {
            PreCondition.AssertNotNull(parentIdChain, "parentIdChain");

            using (DataAccessSection data = EnterDataAccessSection(parentIdChain))
            {
                if (data.CurrentProject == null)
                    return new IDataObject[0];

                CacheWorkItem workItem = data.CurrentProject.TryGetWorkItem(parentIdChain);

                if (workItem != null)
                {
                    return workItem.Attachments.Convert<IDataObject, CacheAttachment>(InitAttachmentObject);
                }
                return new IDataObject[0];
            }
        }

        /// <summary>
        /// Retrieves all data objects which are stored in the current dao.
        /// The structure of the given data objects is defined as follows:
        /// <example>
        /// <code>
        ///  [Parent Id Chain 1] -> Child Instance 1
        ///                      -> Child Instance 2
        ///                      -> Child Instance n
        /// 
        ///  [Parent Id Chain 2] -> Child Instance 3
        ///                      -> Child Instance 4
        ///                      -> Child Instance x
        /// </code>
        /// </example>
        /// </summary>
        /// <returns>Returns the retrieved data objects.</returns>
        public override IEnumerable<KeyValuePair<ValueChain<string>, ICollection<IDataObject>>> GetAll()
        {
            IDictionary<ValueChain<string>, ICollection<IDataObject>> all =
                new Dictionary<ValueChain<string>, ICollection<IDataObject>>();

            using (DataAccessSection data = EnterDataAccessSection())
            {
                var workItemPairs = (from project in data.Connection.DataCache.Projects
                                    from workItemPair in project.WorkItems.Buckets
                                    where workItemPair.Value.Element.Attachments.Count > 0
                                    select workItemPair).ToList();

                foreach (var workItemPair in workItemPairs)
                {
                    all[workItemPair.Key] = GetAll(workItemPair.Key);
                }
            }
            return all;
        }

        /// <summary>
        /// Gets a single data object by its unqiue id.
        /// </summary>
        /// <param name="parentIdChain">Global unique ids of the parent objects.</param>
        /// <param name="id">Id of the data object.</param>
        /// <returns>Returns the retieved element or a null pointer, if it could not be found.</returns>
        public override IDataObject GetById(ValueChain<string> parentIdChain, string id)
        {
            using (DataAccessSection data = EnterDataAccessSection(parentIdChain))
            {
                if (data.CurrentProject == null)
                    return null;

                CacheWorkItem workItem = data.CurrentProject.TryGetWorkItem(parentIdChain);

                if (workItem != null)
                {
                    CacheAttachment iteration = workItem.Attachments.TryGetById(id);

                    if (iteration != null)
                        return InitAttachmentObject(iteration);
                }
            }
            return null;
        }

        private AttachmentDataObject InitAttachmentObject(CacheAttachment attachment)
        {
            return new AttachmentDataObject(this, attachment);
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        /// <summary>
        /// Retrieves a call after the initial data of the back-end system has
        /// been loaded.
        /// </summary>
        internal override void OnInitialized()
        {
            Context.DataCache.CurrentProject.WorkItems.AttachmentChange += OnAttachmentsChange;
        }

        /// <summary>
        /// Retrieves a call after the connection to the back-end system has
        /// been closed.
        /// </summary>
        internal override void OnDisconnected()
        {
            if (Context.DataCache != null
                && Context.DataCache.CurrentProject != null
                && Context.DataCache.CurrentProject.WorkItems != null)
            {
                Context.DataCache.CurrentProject.WorkItems.AttachmentChange -= OnAttachmentsChange;
            }
        }

        private void OnAttachmentsChange(object sender, CacheElementListChangedEventArgs<CacheAttachment> e)
        {
            switch (e.ChangeAction)
            {
                case CacheElementListChangeAction.None:
                    break;
                case CacheElementListChangeAction.Add:
                    OnDataAccessObjectChanged(
                        new DataAccessObjectChangedEventArgs(
                            DataAccessObjectChangedAction.Add,
                            e.ParentChain,
                            InitAttachmentObject(e.Bucket.Element)));
                    break;
                case CacheElementListChangeAction.Remove:
                    OnDataAccessObjectChanged(
                        new DataAccessObjectChangedEventArgs(
                            DataAccessObjectChangedAction.Remove,
                            e.ParentChain,
                            e.Bucket.Element.UniqueId));
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        #endregion
    }
}